#pragma once

#include "Subject.h"
#include "Functor.h"

class TriggerBase
{
public:
	virtual ~TriggerBase()
	{
	}
};

template <class T1, class T2>
class Trigger : public IObserver<T1>, public TriggerBase
{
public:
	Trigger(Subject<T1>* subject, T2* parent)
	{
		if (m_subject)
			m_subject->addObserver(this);
	}
	~Trigger()
	{
		if (m_subject)
			m_subject->removeObserver(this);
	}
	void Notify(const T1 &t, Subject<T1> *o)
	{
		m_parent->Notify( t, o );
	}
	void NotifyOfDeath( Subject<T1> *o)
	{
		m_subject = 0;
	}
private:
	Subject<T1>* m_subject;
	T2* m_parent;
};

template<class T1>
class ValueTrigger : public IObserver<T1>, public TriggerBase
{
	Subject<T1>* m_subject;
	T1 m_object;
public:
	ValueTrigger(Subject<T1> *subject) : m_subject(subject)
	{
		if (m_subject)
		{
			m_subject->addObserver(this);
			m_object = m_subject->getObject();
		}
	}
	~ValueTrigger()
	{
		if (m_subject)
			m_subject->removeObserver(this); 
	}
	void Notify(const T1 &t, Subject<T1> *o)
	{
		m_object = t;
	}
	void NotifyOfDeath( Subject<T1> *o)
	{
		m_subject = 0;
	}
	T1& getObject()
	{
		return m_object;
	}
	T1& operator= (const T1& o)
	{
		m_object = o;
		if (m_subject)
			*m_subject = o;
		return m_object;
	}
};


template<class T1>
class DoubleValueTrigger : public IObserver<T1>, public TriggerBase
{
	Subject<T1>* m_subject1;
	Subject<T1>* m_subject2;
	T1 m_object;
public:
	DoubleValueTrigger(Subject<T1> *subject1, Subject<T1> *subject2) : m_subject1(subject1), m_subject2(subject2)
	{
		if (m_subject1)
		{
			m_subject1->addObserver(this);
		}
		if (m_subject2)
		{
			m_subject2->addObserver(this);
		}
	}
	~DoubleValueTrigger()
	{
		if (m_subject1)
			m_subject1->removeObserver(this); 
		if (m_subject2)
			m_subject2->removeObserver(this); 
	}
	void Notify(const T1 &t, Subject<T1> *o)
	{
		Subject<T1>* subject = (o == m_subject1 ? m_subject2 : m_subject1);		// Get the OTHER subject
		IObserver<T1>::Vector_t& observers = subject->getObservers();			// get the subjects observers
		m_object = t;															// set our value to t												

		
		subject->getObject() = t;												// the the other subjects value to t

		// for all the observers (except this) notify of change
		for (IObserver<T1>::Vector_t::iterator it = observers.begin(); it != observers.end(); ++it)
		{
			if (*it != this)
			{
				(*it)->Notify(t, subject);
			}
		}
	}
	void NotifyOfDeath( Subject<T1> *o )
	{
		if (m_subject1 && m_subject1 == o)
			m_subject1 = 0;
		else if (m_subject2 && m_subject2 == o)
			m_subject2 = 0;
	}
	T1& getObject()
	{
		return m_object;
	}
	T1& operator= (const T1& o)
	{
		m_object = o;
		if (m_subject1)
			*m_subject1 = o;
		if (m_subject2)
			*m_subject2 = o;
		return m_object;
	}
};

template<class T1, class Func>
class DoubleValueTriggerFunctor : public IObserver<T1>, public TriggerBase
{
	typedef void (Func::*funcType)(const T1&);

	Func* m_obj;
	funcType m_func;
	Subject<T1>* m_subject1;
	Subject<T1>* m_subject2;
	T1 m_object;
public:
	DoubleValueTriggerFunctor(Subject<T1> *subject1, Subject<T1> *subject2, Func* obj = 0, funcType func = 0) : m_subject1(subject1), m_subject2(subject2), m_obj(obj), m_func(func)
	{
		if (m_subject1)
		{
			m_subject1->addObserver(this);
		}
		if (m_subject2)
		{
			m_subject2->addObserver(this);
		}
	}
	~DoubleValueTriggerFunctor()
	{
		if (m_subject1)
			m_subject1->removeObserver(this); 
		if (m_subject2)
			m_subject2->removeObserver(this); 
	}
	void Notify(const T1 &t, Subject<T1> *o)
	{
		
		Subject<T1>* subject = (o == m_subject1 ? m_subject2 : m_subject1);		// Get the OTHER subject
		IObserver<T1>::Vector_t& observers = subject->getObservers();			// get the subjects observers
		m_object = t;															// set our value to t
		((m_obj)->*m_func)(t);													// Call our function

		subject->getObject() = t;												// the the other subjects value to t

		// for all the observers (except this) notify of change
		for (IObserver<T1>::Vector_t::iterator it = observers.begin(); it != observers.end(); ++it)
		{
			if (*it != this)
			{
				(*it)->Notify(t, subject);
			}
		}
	}
	void NotifyOfDeath( Subject<T1> *o )
	{
		if (m_subject1 && m_subject1 == o)
			m_subject1 = 0;
		else if (m_subject2 && m_subject2 == o)
			m_subject2 = 0;
	}
	T1& getObject()
	{
		return m_object;
	}
	T1& operator= (const T1& o)
	{
		m_object = o;
		if (m_subject1)
			*m_subject1 = o;
		if (m_subject2)
			*m_subject2 = o;
		return m_object;
	}
};